package org.smartboot.flow.core.builder;


import org.smartboot.flow.core.Condition;
import org.smartboot.flow.core.attribute.AttributeValueResolver;
import org.smartboot.flow.core.attribute.Attributes;
import org.smartboot.flow.core.component.ChooseComponent;
import org.smartboot.flow.core.component.Component;
import org.smartboot.flow.core.executable.Executable;
import org.smartboot.flow.core.util.AssertUtil;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author qinluo
 * @date 2022-11-11 14:54:34
 * @since 1.0.0
 */
public class ChooseBuilder<T, S> extends ExecutableBuilder<T, S> {

    /**
     * PipelineBuilder
     */
    private final PipelineBuilder<T, S> pipelineBuilder;
    private final Condition<T, S> condition;
    private final Map<Object, Component<T, S>> branches = new ConcurrentHashMap<>();

    public ChooseBuilder(Condition<T, S> condition) {
        this.pipelineBuilder = null;
        this.condition = condition;
    }

    public ChooseBuilder(PipelineBuilder<T, S> pipelineBuilder, Condition<T, S> condition) {
        this.pipelineBuilder = pipelineBuilder;
        this.condition = condition;
    }

    public ChooseBuilder<T, S> newBranch(Object branch, Component<T, S> component) {
        AssertUtil.notNull(branch, "branch must not be null");
        AssertUtil.notNull(component, "component must not be null");
        branches.put(branch, component);
        return this;
    }

    public ChooseBuilder<T, S> newBranch(Object branch, Executable<T, S> executable) {
        AssertUtil.notNull(branch, "branch must not be null");
        branches.put(branch, asComponent(executable));
        return this;
    }

    public PipelineBuilder<T, S> end() {
        return end((Component<T, S>)null);
    }

    public PipelineBuilder<T, S> end(Executable<T, S> defaultBranch) {
        return end(asComponent(defaultBranch));
    }

    public ChooseComponent<T, S> build(Component<T, S> defaultBranch) {
        ChooseComponent<T, S> chooseComponent = new ChooseComponent<>(condition, branches, defaultBranch);
        applyValues(chooseComponent);
        return chooseComponent;
    }

    public Component<T, S> build() {
        return build((Component<T, S>)null);
    }

    public Component<T, S> build(Executable<T, S> defaultBranch) {
        return build(asComponent(defaultBranch));
    }

    public PipelineBuilder<T, S> end(Component<T, S> defaultBranch) {
        AssertUtil.notNull(pipelineBuilder, "Only pipeline's sub-builder call this method allowed");
        ChooseComponent<T, S> chooseComponent = new ChooseComponent<>(condition, branches, defaultBranch);
        applyValues(chooseComponent);
        return pipelineBuilder.next(chooseComponent);
    }

    @Override
    public ChooseBuilder<T, S> apply(Attributes attributes, Object value) {
        super.apply(attributes, value);
        return this;
    }

    @Override
    public ChooseBuilder<T, S> withResolver(AttributeValueResolver resolver) {
        super.withResolver(resolver);
        return this;
    }

}
